package com.qf.ticketdemo.controller;

import com.qf.ticketdemo.mapper.TicketMapper;
import com.qf.ticketdemo.pojo.Ticket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/ticket")
public class TicketController2 {

    @Autowired
    private TicketMapper ticketMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 加锁的前缀
     */
    private String LOCK_KEY_PREFIX = "LOCK:KEY:TICKET:ID:";

    @Value("${winnum}")
    private String winnum;

    @GetMapping("/sell/{id}")
    public void sell(@PathVariable("id") Integer id) throws InterruptedException {
        /*
            ① 设置key的超时时间，可以解决因为进程挂掉后的死锁问题
            ② 超时时间到底该设置多少呢？
                业务执行的时间，超过了key的超时时间，导致锁失效。
         */

        //只有在多个人同时买同一趟车次的票，才需要互斥
        String key = LOCK_KEY_PREFIX + id;

        //续时（定时任务 + 守护线程 - 当一个程序中所有的线程都是守护线程时，会自动结束）
        // 看门狗 -- Watch Dog
        Thread t = new Thread(){
            @Override
            public void run() {
                //续时
                while (true){
                    //获取当前剩余的时间
                    Long expire = redisTemplate.getExpire(key);

                    String value = redisTemplate.opsForValue().get(key);

                    //我们续时动作只能给自己加的这个锁续时
                    if (expire<2 && winnum.equals(value)){
                        //重新续时
                        redisTemplate.expire(key,5, TimeUnit.SECONDS);
                    }
                }
            }
        };
        //设置为守护线程
        t.setDaemon(true);
        //开启
        t.start();

        while (true){
            //加分布式锁-setnx（key存在，设置失败；key不存在，设置成功）
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, winnum,5, TimeUnit.SECONDS);

            if (lock) {
                try {
                    //先判断是否有票
                    Ticket ticket = ticketMapper.selectById(id);
                    Integer count = ticket.getCount();
                    if (count > 0) {
                        //卖
                        System.out.println(winnum + "正在卖第" + count + "张票");

                        //模拟卖一张票耗时1s
                        Thread.sleep(8000);

                        //更新数据库
                        ticket.setCount(count - 1);
                        ticketMapper.updateById(ticket);
                    } else {
                        break;
                    }
                }finally {
                    //释放锁 (删除key)
                    redisTemplate.delete(key);
                }
            }

        }
    }
}
